home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 May / macformat-024.iso / Shareware City / Developers / TransSkel Pascal 2.5 / TransSkel / transSkel.p old (2.0) < prev    next >
Encoding:
Text File  |  1988-12-09  |  44.7 KB  |  1,525 lines  |  [TEXT/PJMM]

  1. {    TransSkel version 2.00 - Transportable application skeleton}
  2.  
  3. {    TransSkel is public domain and was originally written in LightSpeed C by:}
  4.  
  5. {            Paul DuBois}
  6. {            Wisconsin Regional Primate Research Center}
  7. {            1220 Capital Court}
  8. {            Madison WI  53706  USA}
  9.  
  10. {    UUCP:    [allegra,ihnp4,seismo]!uwvax!rhesus!dubois }
  11. {    ARPA:    dubois@rhesus.primate.wisc.edu}
  12. {    The Pascal Version of TransSkel is public domain and was ported and changed by        }
  13.  
  14. {            Owen Hartnett            }
  15. {            Ωhm Software            }
  16. {            163 Richard Drive        }
  17. {            Tiverton, RI 02878        }
  18.  
  19. {    CSNET:    omh@cs.brown.edu.CSNET                                             }
  20. {    ARPA:        omh%cs.brown.edu                                                    }
  21. {    UUCP:        [ihnp4,allegra]!brunix !omh                                            }
  22.  
  23. {    This version of TransSkel written for Lightspeed Pascal.  Lightspeed Pascal is a}
  24. {    trademark of:}
  25. {            THINK Technologies, Inc}
  26. {            420 Bedford Street  Suite 350}
  27. {            Lexington, MA  02173  USA}
  28.  
  29. {  History}
  30. {  06/13/86    Beta version. (pd) }
  31. {  08/27/86    Version number changed to 1.01.(pd)}
  32. {              v1.0 DoGrow bug fixed - the port at the point of the}
  33. {              InvalRect could have been anything; the fix is to set}
  34. {              the port to the grown window first.  This also explains}
  35. {              why the kludge to DoActivate in v1.0 worked.(pd)}
  36. {  10/02/86    Version number changed to 1.02, as a result of adding}
  37. {            modifications by David W. Berry (well!dwb@lll-lcc.arpa)}
  38. {            for supporting window zooming.  Also used his modifications}
  39. {            for supporting modeless dialogs (though not in the same}
  40. {            form).  Dialogs can be #define'd on or off.(pd)}
  41.  
  42. {12/ 28 / 86 Version number changed to 1.03 . Modified to work under LightspeedC v . 2.01 }
  43. {            - took out definitions for window zooming stuff , as it is now supported by the compiler}
  44. {             directly . Also declared DoZoom static , fixing an oversight . ( pd )}
  45. { 01 / 18 / 86 Put a SetPort into DoZoom - ZoomWindow requires port to be}
  46. {            set to window being zoomed . ( pd )}
  47. { 02 / 05 / 86 Version number changed to 1.04 . Big change : port setting behavior made explicit}
  48. {             - the only persistant switch occurs when a window comes active . This changes }
  49. {            underlying programming model ( see manual for detailed discussion ) . Thanks to}
  50. {             Duane Williams for pointing out that this should be done . Typedef 'd }
  51. {            integer/long variables to Integer, Longint to facilitate coversion to other C }
  52. {            compilers . More complete type-casting done . LightspeedC does a lot of it }
  53. {            automatically , other compilers may not . ( pd - this version never released ) }
  54. {03 / 02 / 87 Fixed bug whereby clicks in drag region of non - active windows may not bring }
  55. {            window to front . Seems to be due to DragWindow calling StillDown to see    if mouse is still }
  56. {            down .    If the machine was busy    otherwise when click occurred and }
  57. {            mouse already up when DragWindow is called , the click ends up being ignored . }
  58. {            Thanks to Roger Humphrey for finding this one . }
  59.  
  60. {* * * Changes implemented first by omh to Pascal Version}
  61.  
  62. { 12/24/86  Finished first Pascal version.   Dialogs cannot be defined off.     (omh)}
  63.  
  64. {4 / 18 / 87 Changed Desk Accessory code so it 's more tolerant of memory}
  65. {            conditions for desk accessories . ( omh ) }
  66. {7 / 12 / 87 Added "cache " code to GetWDHandler . Now TransSkel figures }
  67. {            that an event is most likely to occur for the same window as the previous }
  68. {            event . Thus the WindowPtr and WDHandle for events are cached and examined }
  69. {            to avoid searching through the handler list . ( omh ) }
  70. {7 / 12 / 87 Excised the notorious "SetPort "excess . As pointed out by Duane Williams ,}
  71. {             SetPort traps abounded unnecessarily in version 1.02 . These have been eliminated }
  72. {            now with two exceptions . First , the port is set when a window handler }
  73. {            is installed . The justification for this is that when a handler is installed , it }
  74. {            is likely that further processing will be done on it immediately . The application gets }
  75. {            control immediately after the handler is installed anyway , so this behavior can be manually }
  76. {            overridden where necessary . Second , when a window is activated , the port is}
  77. {            set to it . This follows the model of keeping the port in sync with the }
  78. {            active window . ( omh ) }
  79. {7 / 14 / 87 Added grow zone function installation    and MoreMasters to SkelInit , }
  80. {            which now requires two parameters . The first indicates the number of times to call }
  81. {            MoreMasters . The second is a ProcPtr indicating a user - supplied grow zone}
  82. {            function to be called when memory problems occur .  If nil , no grow zone}
  83. {            function is    installed . ( omh ) }
  84. {7 / 14 / 87 SkelMenu , SkelWindow , and SkelDialog now return zero or non - zero to indicate }
  85. {            failure or success of handler allocation . This could break * all * previous TransSkel }
  86. {            applications ( as will the change to SkelInit , above . Please see the section "How to }
  87. {            adapt old TransSkel to New "    in the manual    for detailed specifications on }
  88. {            how to convert your old programs . TransSkel becomes more memory conscious}
  89. {            with these changes . The functions SkelMenu , SkelWindow , and }
  90. {            SkelDialog are the only routines which actually allocate memory . Since they may be }
  91. {            called at any time , knowing that you have enough memory becomes important . Thus , }
  92. {            these routines return a value to indicate what happened .  If they return zero ,}
  93. {             then memory allocation failed . ( omh ) }
  94. {10 / 21 / 87 Added another parameter to SkelMenu:  drawBar: Boolean . This tells SkelMenu }
  95. {            whether to draw the menu bar after adding the Menu . This is done to eliminate }
  96. {            the menus popping up one at a time . Simply call SkelMenu  with drawBar false}
  97. {            until the    last time you call SkelMenu , then call it (for the last menu )}
  98. {            with drawBar true . ( omh ) }
  99. {10 / 26 / 87 Removed declarations for zoom - in and zoom - out . Added Pascal }
  100. {            changes ( above ) to C version . ( omh )}
  101.  
  102. {    02 / 02 / 88 Merged pd 's 1.04 changes with those of omh, above, to create}
  103. {            release version 2.0 . Fixed bug whereby cmd - key equivalents}
  104. {            for menu selections would execute twice if DA window in front . Thanks }
  105. {            to Don Fredkin and Julian Vrieslander for finding this one , and to Don for the}
  106. {             best fix . ( pd ) }
  107. { 10/28/88 Removed all New Rom calls. }
  108. { 10/28/88 Added support for conditional compilation for dialogs and MPW support.  By setting }
  109. {            the Think_pascal flag to false, TransSkel will run under MPW. Now correctly written for LSP 2.0}
  110.  
  111.  
  112. unit TransSkel;
  113.  
  114. interface
  115.  
  116. {$SETC supportDialogs:= true }
  117.                     { Set to false to disallow modeless dialog support and save code space }
  118.                 { Set to false to have SkelInit call QuickDraw Inits: InitGraf, InitDialog, etc. }
  119.  
  120. {$IFC UNDEFINED THINK_PASCAL}
  121.  
  122.     uses
  123.         Memtypes, Quickdraw, OSIntf, ToolIntf, PackIntf;
  124. {$ENDC}
  125.  
  126.     procedure SkelInit (NoMasters: integer; myGrowZone: ProcPtr);
  127.     procedure SkelMain;
  128.     procedure SkelWhoa;
  129.     procedure SkelClobber;
  130.     function SkelMenu (theMenu: MenuHandle; pSelect: ProcPtr; pClobber: ProcPtr; DrawBar: Boolean): Boolean;
  131.     procedure SkelRmveMenu (theMenu: MenuHandle);
  132.     procedure SkelApple (aboutTitle: Str255; aboutProc: ProcPtr);
  133.     function SkelWindow (theWind: WindowPtr; pMouse, pKey, pUpdate, pActivate, pClose, pClobber, pIdle: ProcPtr; frontOnly: Boolean): Boolean;
  134.     procedure SkelRmveWind (theWind: WindowPtr);
  135. {$IFC supportDialogs }
  136.     function SkelDialog (theDialog: DialogPtr; pEvent, pClose, pClobber: ProcPtr): Boolean;
  137.     procedure SkelRmveDlog (theDialog: DialogPtr);
  138. {$ENDC}
  139.     procedure SkelGrowBounds (theWind: WindowPtr; hLO, vLo, hHi, vHi: integer);
  140.     procedure SkelEventMask (mask: integer);
  141.     procedure SkelGetEventMask (var mask: integer);
  142.     procedure SkelBackground (p: ProcPtr);
  143.     procedure SkelGetBackground (var p: ProcPtr);
  144.     procedure SkelEventHook (p: ProcPtr);
  145.     procedure SkelGetEventHook (var p: ProcPtr);
  146. {$IFC supportDialogs }
  147.     procedure SkelDlogMask (mask: integer);
  148.     procedure SkelGetDlogMask (var mask: integer);
  149. {$ENDC}
  150.  
  151.  
  152. implementation
  153.  
  154.     const
  155.         mBarHeight = 20;    { menu bar height.  All window sizing}
  156.  
  157.         GrowZoneSize = 4000;        {  Size of memory to be freed when GrowZone Proc called }
  158.  
  159. {    This window zooming stuff may need to be removed if you use the new Rom libraries        }
  160. {    if not, then you can add zooming without the overhead of the new Rom libs.  See TrackBox    }
  161. {     routine also.                                                                                            }
  162.  
  163. {    Window and Menu handler types, constants, variables.}
  164.  
  165. {    whList and mhList are the lists of window and menu handlers.}
  166. {    whClobOnRmve and mhClobOnRmve are true if the handler disposal proc}
  167. {    is to be called when a handler is removed.  They are temporarily set}
  168. {    false when handlers are installed for windows or menus that already}
  169. {    have handlers - the old handler is removed WITHOUT calling the}
  170. {    disposal proc.}
  171.  
  172. {    Default lower limits on window sizing of 80 pixels both directions is}
  173. {    sufficient to allow text windows room to draw a grow box and scroll}
  174. {    bars without having the thumb and arrows overlap.  These values may}
  175. {    be changed if such a constraint is undesirable with SkelGrowBounds.}
  176. {    Default upper limits are for the Macintosh, not the Lisa, but are set}
  177. {    per machine in SkelInit.}
  178.  
  179.     type
  180.         WHandlerPtr = ^WHandler;
  181.         WHandlerHnd = ^WHandlerPtr;
  182.         WHandler = record
  183.                 whWind: WindowPtr;    {window/dialog to be handled    }
  184.                 whClobber: ProcPtr;    { data structure disposal proc    }
  185.                 whMouse: ProcPtr;        { mouse-click handler proc        }
  186.                 whKey: ProcPtr;        { key-click handler proc            }
  187.                 whUpdate: ProcPtr;        { update handler proc                }
  188.                 whActivate: ProcPtr;    { activate event handler proc    }
  189.                 whClose: ProcPtr;        { close "event" handler proc        }
  190.                 whIdle: ProcPtr;            { main loop proc                    }
  191. {$IFC supportDialogs }
  192.                 whEvent: ProcPtr;        { dialog event proc                }
  193. {$ENDC }
  194.                 whHasGrow: Boolean;    { can window grow?                }
  195.                 whGrow: Rect;            { limits on window sizing        }
  196.                 whSized: Boolean;        { true = window was resized    }
  197.                 whFrontOnly: Boolean;    { true = idle only when active    }
  198.                 whNext: WHandlerHnd;            { next window handler            }
  199.             end;
  200.  
  201.         MHandlerPtr = ^MHandler;
  202.         MHandlerHnd = ^MHandlerPtr;
  203.  
  204.         MHandler = record
  205.                 mhID: integer;                { menu id                                    }
  206.                 mhSelect: ProcPtr;            { item selection handler proc            }
  207.                 mhClobber: ProcPtr;        { menu disposal handler proc            }
  208.                 mhNext: MHandlerHnd;        { next menu handler                        }
  209.             end;
  210.  
  211.     var
  212.         whList: WHandlerHnd;                { list of menu handlers }
  213.         whClobOnRmve: Boolean;
  214.         growRect: Rect;
  215.         mhList: MHandlerHnd;
  216.         mhClobOnRmve: Boolean;
  217.  
  218. {    Variables for default Apple menu handler.  appleID is set to 1 if}
  219. {    SkelApple is called and is the id of the Apple menu, appleAboutProc}
  220. {    is the procedure to execute if there is an About... item and it's}
  221. {    chosen from the Apple menu.  If doAbout is true, then the menu}
  222. {    contains the About... item, otherwise it's just desk accessories.}
  223.  
  224.         appleMenu: MenuHandle;
  225.         appleID: integer;
  226.         appleAboutProc: ProcPtr;
  227.         doAbout: Boolean;
  228.  
  229. {    Miscellaneous}
  230.  
  231. {    screenPort points to the window manager port.}
  232.  
  233. {    doneFlag determines when SkelMain returns.  It is set by calling}
  234. {    SkelWhoa(), which the host does to request a halt.}
  235.  
  236. {    pBkgnd points to a background procedure, to be run during event}
  237. {    processing.  Set it with SkelBackground.  If nil, there's no}
  238. {    procedure.}
  239.  
  240. {    pEvent points to an event-inspecting hook, to be run whenever an}
  241. {    event occurs.  Set it with SkelEventHook.  If nil, there's no}
  242. {    procedure.}
  243.  
  244. {    eventMask controls the event types requested in the GetNextEvent}
  245. {    call in SkelMain.}
  246.  
  247. {    diskInitPt is the location at which the disk initialization dialog}
  248. {    appears, if an uninitialized disk is inserted.}
  249.  
  250.         screenPort: GrafPtr;
  251.         doneFlag: integer;
  252.         pBkgnd: ProcPtr;
  253.         pEvent: ProcPtr;
  254.         eventMask: integer;
  255.         diskInitPt: Point;
  256.  
  257. {$IFC supportDialogs }
  258.  
  259. {    Events that are passed to dialogs.  Others are ignored.}
  260. {    Standard mask passes , mousedown, keydown, autokey, update,}
  261. {    activate and null events.  Null events are controlled by bit 0.}
  262.  
  263.         dlogEventMask: integer;
  264. {$ENDC}
  265.         pEventflag: Boolean;
  266.  
  267. {    "caching" global variables.  previous version would search down the window }
  268. {    list for every event it found.  Now, if the event happened to the same window }
  269. {    as last time, GetWDHandler will just do a simple compare }
  270. {    and return the last window handler.  This speeds up multiple window applications }
  271. {    immensely, at only a slight cost when you activate a new window (one }
  272. {    compare!)  If you don't like it, use the old version. }
  273.  
  274.         oldWindow: WindowPtr;
  275.         oldWDHandler: WHandlerHnd;
  276.  
  277. {    Global for built in "Grow Zone" function  }
  278.  
  279.         safetyHandle: Handle;
  280.  
  281.         myDitl: packed array[0..100] of byte;
  282.  
  283. { Rather than including the entire new ROM libraries, with all the other stuff you might not use    }
  284. { I've instead included just the Zoom box stuff here.  Depending on your status, you can either    }
  285. { leave things as they are, and only use zooming from the new Rom libs, or comment out the        }
  286. { calls, and include the new Rom libraries if you want to incorporate other new Rom calls        }
  287.  
  288.  
  289. { -------------------------------------------------------------------- }
  290. {                        Internal (private) Routines                                    }
  291. { -------------------------------------------------------------------- }
  292.  
  293.  
  294.  
  295. {    Get handler associated with user or dialog window.}
  296. {    Return nil if window doesn't belong to any known handler.}
  297. {    This routine is absolutely fundamental to TransSkel.}
  298.  
  299.     function GetWDHandler (theWind: WindowPtr): WHandlerHnd;
  300.  
  301.         var
  302.             h: WHandlerHnd;
  303.     begin
  304.         h := WhList;
  305.         GetWDHandler := nil;
  306.         if theWind = oldWindow then            {  caching code     }
  307.             GetWDHandler := oldWDHandler
  308.         else
  309.             while h <> nil do
  310.                 if h^^.whWind = theWind then
  311.                     begin
  312.                         oldWindow := theWind;            { Load in new values for new window }
  313.                         oldWDHandler := h;
  314.                         GetWDHandler := h;
  315.                         h := nil;
  316.                     end
  317.                 else
  318.                     h := WHandlerHnd(h^^.whNext);
  319.     end;
  320.  
  321. { Get Handler associated with user window.  Return nil if window doesn't}
  322. {  have a Handler. }
  323.  
  324.     function GetWHandler (theWind: WindowPtr): WHandlerHnd;
  325.  
  326.         var
  327.             h: WHandlerHnd;
  328.             myPeek: WindowPeek;
  329.  
  330.     begin
  331.         h := GetWDHandler(theWind);
  332.         myPeek := WindowPeek(theWind);
  333.         if h <> nil then
  334.             begin
  335.                 if mypeek^.windowKind <> dialogKind then
  336.                     GetWHandler := h;
  337.             end
  338.         else
  339.             GetWHandler := nil;
  340.     end;
  341.  
  342. {$IFC supportDialogs }
  343.  
  344. {    Get handler associated with dialog window.}
  345. {    Return nil if window doesn't belong to any known handler.}
  346.  
  347.     function GetDHandler (theDialog: WindowPtr): WHandlerHnd;
  348.  
  349.         var
  350.             h: WHandlerHnd;
  351.             myPeek: WindowPeek;
  352.  
  353.     begin
  354.         h := GetWDHandler(theDialog);
  355.         myPeek := WindowPeek(theDialog);
  356.         if h <> nil then
  357.             begin
  358.                 if mypeek^.windowKind = dialogKind then
  359.                     GetDHandler := h;
  360.             end
  361.         else
  362.             GetDHandler := nil;
  363.     end;
  364. {$ENDC}
  365.  
  366. {The following procedures are Pascal "glue" that allows Pascal to call a Procedure    }
  367. { from a ProcPtr.  It is similar to (*p) () construct used in the C dialect.  Different    }
  368. { procedures are necessary for the reason of Pascal's strongly typed parameter        }
  369. { list.  Fortunately, there are not too many calls which use different param lists        }
  370.  
  371.     procedure callpMouse (thePoint: Point; theTime: longint; theMods: integer; myProc: ProcPtr);
  372.  
  373.     inline
  374.         $205f,     {movea.l  (a7)+,a0        ; (a0) is a ptr to string, 4(a0) is mode}
  375.         $4e90;
  376.  
  377.     procedure callpKey (theChar: char; theMods: integer; myProc: ProcPtr);
  378.  
  379.     inline
  380.         $205f,     {movea.l  (a7)+,a0        ; (a0) is a ptr to string, 4(a0) is mode}
  381.         $4e90;
  382.  
  383.     procedure callpEvent (theitem: integer; var theEvent: EventRecord; myProc: ProcPtr);
  384.  
  385.     inline
  386.         $205f,     {movea.l  (a7)+,a0        ; (a0) is a ptr to string, 4(a0) is mode}
  387.         $4e90;
  388.  
  389.     function callotherEvent (var theEvent: EventRecord; myProc: ProcPtr): Boolean;
  390.  
  391.     inline
  392.         $205f, $4e90;
  393.  
  394.  
  395.     procedure callpBoolean (myBool: Boolean; myProc: ProcPtr);
  396.  
  397. { Two calls use Booleans as one parameter arguments.  This procedure handles    }
  398. { both of them.                                                                            }
  399.  
  400.     inline
  401.         $205f,     {movea.l  (a7)+,a0        ; (a0) is a ptr to string, 4(a0) is mode}
  402.         $4e90;
  403.  
  404.     procedure callpInt (myInt: integer; myProc: ProcPtr);
  405.  
  406. { Two calls use Booleans as one parameter arguments.  This procedure handles    }
  407. { both of them.                                                                            }
  408.  
  409.     inline
  410.         $205f,     {movea.l  (a7)+,a0        ; (a0) is a ptr to string, 4(a0) is mode}
  411.         $4e90;
  412.  
  413.     procedure callpMenu (myMenu: MenuHandle; myProc: ProcPtr);
  414.  
  415. { Handle removeal of menus.     }
  416.  
  417.     inline
  418.         $205f,     {movea.l  (a7)+,a0        ; (a0) is a ptr to string, 4(a0) is mode}
  419.         $4e90;
  420.  
  421.     procedure callpnoarg (myProc: ProcPtr);
  422.  
  423. { For all the Procedures that are called with no arguments                            }
  424.  
  425.     inline
  426.         $205f,     {movea.l  (a7)+,a0        ; (a0) is a ptr to string, 4(a0) is mode}
  427.         $4e90;
  428.  
  429. {    General menu-handler.  Just passes selection to the handler's}
  430. {    select routine.  If the select routine is nil, selecting items from}
  431. {    the menu is a nop.}
  432.  
  433.     procedure DoMenuCommand (command: longint);
  434.  
  435.         var
  436.             menu: integer;
  437.             item: integer;
  438.             mh: MHandlerHnd;
  439.             p: ProcPtr;
  440.  
  441.     begin
  442.         menu := HiWord(command);
  443.         item := LoWord(command);
  444.         mh := mhList;
  445.         while (mh <> nil) do
  446.             begin
  447.                 p := mh^^.mhSelect;
  448.                 if ((menu = mh^^.mhID) and (p <> nil)) then
  449.                     begin
  450.                         callpInt(item, p);
  451.                         mh := nil;
  452.                     end
  453.                 else
  454.                     mh := mh^^.mhNext;
  455.             end;
  456.         HiliteMenu(0);
  457.     end;
  458.  
  459. {    Apple menu handler}
  460.  
  461. {    DoAppleItem:  If the first item was chosen, and there's an "About..."}
  462. {    item, call the procedure associated with it (if not nil).  If there}
  463. {    is no "About..." item or the item was not the first one, then open}
  464. {    the associated desk accessory.  The port is saved and restored}
  465. {    because OpenDeskAcc does not always preserve it correctly.}
  466.  
  467. {    DoAppleClobber disposes of the Apple menu.}
  468.  
  469.     procedure DoAppleItem (item: integer);
  470.  
  471.         var
  472.             curPort: GrafPtr;
  473.             str: Str255;
  474.             ignore: integer;
  475.             h: Handle;
  476.  
  477.     begin
  478.         if doAbout and (item = 1) then
  479.             begin
  480.                 if appleAboutProc <> nil then
  481.                     callpnoarg(appleAboutProc);
  482.             end
  483.         else
  484.             begin
  485.                 GetPort(curPort);
  486.                 GetItem(appleMenu, item, str);
  487.                 SetResLoad(false);
  488.                 h := GetNamedResource('DRVR', str);
  489.                 SetResLoad(true);
  490.                 if h <> nil then
  491.                     begin
  492.                         ResrvMem(SizeResource(h) + $1000);
  493.                         ignore := OpenDeskAcc(str);
  494.                     end;
  495.                 SetPort(curPort);
  496.             end;
  497.     end;
  498.  
  499.     procedure DoAppleClobber;
  500.     begin
  501.         DisposeMenu(appleMenu);
  502.     end;
  503.  
  504. { --------------------------------------------------------------------     }
  505. {                        Window-handler routing routines                            }
  506. {                                                                                        }
  507. {    Each routine sets the port to the handler's window before executing        }
  508. {    the handler procedure.                                                            }
  509. { --------------------------------------------------------------------     }
  510.  
  511.  
  512. {    Pass local mouse coordinates, click time, and the modifiers flag}
  513. {    word to the handler.  Should not be necessary to set the port, as}
  514. {       the click is passed to the active window's hander. }
  515.  
  516.     procedure DoMouse (h: WHandlerHnd; theEvent: EventRecord);
  517.  
  518.         var
  519.             p: ProcPtr;
  520.             thePt: Point;
  521.  
  522.     begin
  523.         if (h <> nil) then
  524.             begin
  525.                 p := h^^.whMouse;
  526.                 if p <> nil then
  527.                     begin
  528.                         thePt := theEvent.where;
  529.                         GlobalToLocal(thePt);
  530.                         callpMouse(thePt, theEvent.when, theEvent.modifiers, p);
  531.                     end;
  532.             end;
  533.     end;
  534.  
  535. {    Pass the character and the modifiers flag word to the handler.}
  536. {    Should not be necessary to set the port, as the  click is passed to the}
  537. {    active window's handler. }
  538.  
  539.     procedure DoKey (h: WHandlerHnd; ch: char; mods: integer);
  540.         var
  541.             p: ProcPtr;
  542.  
  543.     begin
  544.         if h <> nil then
  545.             begin
  546.                 p := h^^.whKey;
  547.                 if p <> nil then
  548.                     callpKey(ch, mods, p);
  549.             end;
  550.     end;
  551.  
  552. {    Call the window updating procedure, passing to it an indicator whether the}
  553. {    window has been resized or not.  Then clear the flag, assuming the update}
  554. {    proc took whatever action was necessary to respond to resizing.}
  555. {}
  556. {    If the handler doesn't have any update proc, the Begin/EndUpdate stuff}
  557. {    is still done, to clear the update region.  Otherwise the Window Manager }
  558. {    will keep generating update events for the window, stalling updates of}
  559. {    other windows.    }
  560.  
  561. {    Make sure to save and restore the port, as it's not always the active window}
  562. {    that's updated.    }
  563.  
  564.     procedure DoUpdate (h: WHandlerHnd);
  565.  
  566.         var
  567.             rh: WhandlerHnd;
  568.             p: ProcPtr;
  569.             updPort, tmpPort: GrafPtr;
  570.  
  571.     begin
  572.         rh := h;
  573.         if rh <> nil then
  574.             begin
  575.                 GetPort(tmpPort);
  576.                 updPort := rh^^.whWind;
  577.                 SetPort(updPort);
  578.                 BeginUpdate(updPort);
  579.                 p := rh^^.whUpdate;
  580.                 if p <> nil then
  581.                     begin
  582.                         callpBoolean(rh^^.whSized, p);
  583.                         rh^^.whSized := false;
  584.                     end;
  585.                 EndUpdate(updPort);
  586.                 SetPort(tmpPort);
  587.             end;
  588.     end;
  589.  
  590. {    Pass activate/deactivate notification to handler.  On activate, set the port to}
  591. {    the window coming active    }
  592.  
  593.     procedure DoActivate (h: WHandlerHnd; active: Boolean);
  594.  
  595.         var
  596.             p: ProcPtr;
  597.  
  598.     begin
  599.         if h <> nil then
  600.             begin
  601.                 if active then
  602.                     SetPort(h^^.whWind);
  603.                 p := h^^.whActivate;
  604.                 if p <> nil then
  605.                     callpBoolean(active, p);
  606.             end
  607.     end;
  608.  
  609. {    Execute a window handler's close proc.  The close box for handlers}
  610. {    for temp windows that want to remove themselves when the window}
  611. {    is closed can call SkelRmveWind to dispose of the window}
  612. {    and remove the handler from the window handler list.  Thus, windows}
  613. {    may be dynamically created and destroyed without filling up the}
  614. {    handler list with a bunch of invalid handlers.}
  615.  
  616. {    If the handler doesn't have a close proc, just hide the window.}
  617. {    The host should provide some way of reopening the window (perhaps}
  618. {    a menu selection).  Otherwise the window will be lost from user}
  619. {    control if it is hidden, since it won't receive user-initiated events.}
  620.  
  621. {    Since the close box of only the active window may be clicked, it}
  622. {    is not necessary to set the port . }
  623.  
  624. {    This is called both for regular and dialog windows.}
  625.  
  626.     procedure DoClose (h: WHandlerHnd);
  627.  
  628.         var
  629.             rh: WHandlerHnd;
  630.             p: ProcPtr;
  631.     begin
  632.         rh := h;
  633.         if rh <> nil then
  634.             begin
  635.                 p := rh^^.whClose;
  636.                 if (p <> nil) then
  637.                     callpnoarg(p)
  638.                 else
  639.                     HideWindow(rh^^.whWind);
  640.             end;
  641.     end;
  642.  
  643. {    Execute a window Handler's clobber proc.  This is called both for regular and dialog windows.}
  644. {    Must save, set and restore port, since any window (not just active one) may be clobbered    }
  645. {    at any time.}
  646. {}
  647. {    Don't need to check whether handler is nil, as in other handler procedures, since this is only}
  648. {    called by SkelRmveWind with a known valid handler.     }
  649.  
  650.     procedure DoClobber (h: WHandlerHnd);
  651.  
  652.         var
  653.             p: ProcPtr;
  654.             curPort: Grafptr;
  655.     begin
  656.         if (h <> nil) then
  657.             begin
  658.                 GetPort(curPort);
  659.                 SetPort(h^^.whWind);
  660.                 p := h^^.whClobber;
  661.                 if p <> nil then
  662.                     callpnoarg(p);
  663.                 SetPort(curPort);
  664.             end;
  665.     end;
  666.  
  667. {$IFC supportDialogs }
  668.  
  669. {    Handle event if it's for a dialog.  The event must be one of}
  670. {    those that is passed to dialogs according to dlogEventMask.}
  671. {    This mask can be set so that disk-inserts, for instance, don't}
  672. {    get eaten up.}
  673.  
  674.     function DoDialog (theEvent: EventRecord): Boolean;
  675.  
  676.         var
  677.             dh: WHandlerHnd;
  678.             theDialog: DialogPtr;
  679.             myDPeek: DialogPeek;
  680.             what: integer;
  681.             item: integer;
  682.             tmpPort: GrafPtr;
  683.             ignore: Boolean;
  684.             testme: longint;
  685.  
  686.     begin
  687.  
  688. {    handle command keys before they get to IsDialogEvent}
  689.  
  690.         what := theEvent.what;
  691.         testme := BitShift(longint(1), what);
  692.         testme := BitAnd(testme, longint(dlogEventMask));
  693.         if (((what = keydown) or (what = autokey)) and Boolean(BitAnd(theEvent.modifiers, cmdkey))) then
  694.             begin
  695.                 DoMenuCommand(MenuKey(Char(BitAnd(theEvent.message, charCodeMask))));
  696.                 DoDialog := true;
  697.             end
  698.         else if testme > 0 then
  699.             if IsDialogEvent(theEvent) then
  700.                 begin
  701.                     if DialogSelect(theEvent, theDialog, item) then
  702.                         begin
  703.                             dh := WHandlerHnd(GetDHandler(theDialog));
  704.                             if (dh <> nil) then
  705.                                 if (dh^^.whEvent <> nil) then
  706.                                     begin
  707.                                         GetPort(tmpPort);
  708.                                         SetPort(theDialog);
  709.                                         callpEvent(item, theEvent, dh^^.whEvent);
  710.                                         SetPort(tmpPort);
  711.                                     end;
  712.                         end;
  713.                     DoDialog := true;
  714.                 end
  715.             else
  716.                 DoDialog := false;
  717.     end;
  718. {$ENDC}
  719.  
  720. { -------------------------------------------------------------------- }
  721. {                            Event-handling routines                        }
  722. { -------------------------------------------------------------------- }
  723.  
  724. {    Have either sized or zoomed the window.  Invalidate it to force}
  725. {    an update and set the 'resized' flag in the window handler true.}
  726. {    The port is assumed to be set to the port that changed size.        }
  727.  
  728.     procedure TriggerUpdate (h: WHandlerHnd; thePort: GrafPtr);
  729.  
  730.     begin
  731.         InvalRect(thePort^.portRect);
  732.         if (h <> nil) then
  733.             begin
  734.                 h^^.whSized := true;
  735.             end;
  736.     end;
  737.  
  738. {    Size a window.  If the window has a handler, use the grow limits}
  739. {    in the handler record, otherwise use the defaults.}
  740.  
  741. {    The portRect is invalidated to force an update event.    The handler's}
  742. {    update procedure should check the parameter passed to it to check}
  743. {    whether the window has changed size, if it needs to adjust itself to}
  744. {    the new size.  THIS IS A CONVENTION.  Update procs must notice grow}
  745. {    "events", there is no procedure specifically for such events.}
  746.  
  747. {    The clipping rectangle is not reset.  If the host application}
  748. {    keeps the clipping set equal to the portRect or something similar,}
  749. {    then it will have to arrange to treat window growing with more}
  750. {    care.}
  751. {}
  752. {    Since the grow region of only the active window may be clicked, it should}
  753. {    not be necessary to set the port.}
  754.  
  755.     procedure DoGrow (h: WHandlerHnd; thePort: GrafPtr; StartPt: Point);
  756.  
  757.         var
  758.             r: Rect;
  759.             growRes: longint;
  760.  
  761.     begin
  762.         if (h <> nil) then
  763.             begin
  764.                 r := h^^.whGrow;
  765.             end
  766.         else
  767.             r := growRect;
  768.         growRes := GrowWindow(thePort, startPt, r);
  769.         if growRes <> 0 then
  770.             begin
  771.                 SizeWindow(thePort, LoWord(growRes), HiWord(growRes), false);
  772.                 TriggerUpdate(h, thePort);
  773.             end;
  774.     end;
  775.  
  776.  
  777. {    Zoom the current window.  Very similar to DoGrow}
  778. {    Since the zoombox of only the active window may be clicked, it should not be necessary}
  779. {    to set the port.    }
  780.  
  781.     procedure DoZoom (h: WHandlerHnd; thePort: GrafPtr; partcode: integer);
  782.  
  783.     begin
  784.         ZoomWindow(thePort, partcode, false);
  785.         TriggerUpdate(h, thePort);
  786.     end;
  787.  
  788. {    General event handler}
  789.  
  790.     procedure DoEvent (theEvt: Eventrecord);
  791.  
  792.         var
  793.             theEvent: EventRecord;
  794.             evtPt: Point;
  795.             evtPort: GrafPtr;
  796.             evtPart: integer;
  797.             evtChar: char;
  798.             evtMods: integer;
  799.             h: WHandlerHnd;
  800.             r: Rect;
  801.             ignore: integer;
  802.  
  803.     begin
  804.         theEvent := theEvt;
  805. {$IFC supportDialogs }
  806.         if not (DoDialog(theEvent)) then
  807. {$ENDC}
  808.             begin
  809.                 evtPt := theEvent.where;
  810.                 case theEvent.what of
  811.                     nullEvent: 
  812.                         ;
  813.  
  814. {    Mouse click.  Get the window that the click occurred in, and the}
  815. {    part of the window.    Get WDHandler is called here, not GetWHandler, since}
  816. {    we need the handler for a window which might turn out to be a dialog window,}
  817. {    e.g., if the click is in a close box.}
  818.  
  819.                     mouseDown: 
  820.                         begin
  821.                             evtPart := FindWindow(evtPt, evtPort);
  822.                             h := GetWDHandler(evtPort);
  823.                             case evtPart of
  824.  
  825. {    Click in a desk accessory window.  Pass back to the system.}
  826.  
  827.                                 inSysWindow: 
  828.                                     SystemClick(theEvent, evtPort);
  829.  
  830. {    Click in menu bar.  Track the mouse and execute selected command,}
  831. {    if any.}
  832.  
  833.                                 inMenuBar: 
  834.                                     DoMenuCommand(MenuSelect(evtPt));
  835.  
  836. {    Click in grow box.  Resize window.}
  837.  
  838.                                 inGrow: 
  839.                                     DoGrow(h, evtPort, evtPt);
  840.  
  841. {    Click in title bar.  Drag the window around.  Leave at least}
  842. {    4 pixels visible in both directions.    Bug fix: The window, if not front, is}
  843. {    selected first to make sure it's at least activated (unless the command key is down - see Inside}
  844. {    Macintosh).  DragWindow seems to call StillDown first, so that clicks in drag regions while}
  845. {    machine is busy don't otherwise bring window to front if the mouse is already up by the time}
  846. {    DragWindow is called.}
  847.  
  848.                                 inDrag: 
  849.                                     begin
  850.                                         if (evtPort <> FrontWindow) and (BitAnd(evtmods, cmdKey) = 0) then
  851.                                             SelectWindow(evtPort);
  852.                                         r := screenPort^.portRect;
  853.                                         r.top := r.top + mBarHeight;        { Skip down past menu bar    }
  854.                                         InsetRect(r, 4, 4);
  855.                                         DragWindow(evtPort, evtPt, r);
  856.                                     end;
  857.  
  858. {    Click in close box.  Call the close proc if the window has one.}
  859.  
  860.                                 inGoAway: 
  861.                                     if (TrackGoAway(evtPort, evtPt)) then
  862.                                         DoClose(GetWDHandler(evtPort));
  863.  
  864. {    Click in content region.  If the window wasn't frontmost (active),}
  865. {    just select it, otherwise pass the click to the window's mouse}
  866. {    click handler.}
  867.  
  868.                                 inContent: 
  869.                                     if (evtPort <> FrontWindow) then
  870.                                         SelectWindow(evtPort)
  871.                                     else
  872.                                         DoMouse(h, theEvent);
  873.  
  874. {    Click in zoom box.  Track the click and then zoom the window if}
  875. {    necessary}
  876.  
  877.                                 inZoomin, inZoomOut: 
  878.                                     if (TrackBox(evtPort, evtPt, evtPart)) then
  879.                                         DoZoom(h, evtport, evtPart);
  880.                                 otherwise
  881.                                     ;
  882.                             end;{mousedown}
  883.                         end;
  884.  
  885. {    Key event.  If the command key was down, process as menu item}
  886. {    selection, otherwise pass the character and the modifiers flags}
  887. {    to the active window's key handler.}
  888.  
  889. {    If dialogs are supported, there's no check for command-key}
  890. {    equivalents, since that would have been checked in DoDialog.}
  891.  
  892.                     keydown, autokey: 
  893.                         begin
  894.                             evtChar := char(BitAnd(theEvent.message, charCodeMask));
  895.                             evtMods := theEvent.modifiers;
  896.                             if BitAnd(evtMods, cmdKey) > 0 then
  897.                                 DoMenuCommand(menuKey(evtChar))
  898.                             else
  899.                                 DoKey(GetWHandler(FrontWindow), evtChar, evtMods);
  900.                         end;
  901.  
  902. {    Update a window.}
  903.  
  904.                     updateEvt: 
  905.                         DoUpdate(GetWHandler(WindowPtr(theEvent.message)));
  906.  
  907. {    Activate or deactivate a window.}
  908.  
  909.                     activateEvt: 
  910.                         DoActivate(GetWHandler(WindowPtr(theEvent.message)), (BitAnd(theEvent.modifiers, activeFlag) <> 0));
  911.  
  912. {    handle inserts of uninitialized disks}
  913.  
  914.                     diskEvt: 
  915.                         if (HiWord(theEvent.message) <> noErr) then
  916.                             begin
  917.                                 DILoad;
  918.                                 ignore := DIBadMount(diskInitPt, theEvent.message);
  919.                                 DIUnload;
  920.                             end;
  921.                     otherwise
  922.                 end;
  923.             end;
  924.     end;
  925.  
  926. { -------------------------------------------------------------------- }
  927. {                        Interface (public) Routines                        }
  928. { -------------------------------------------------------------------- }
  929.  
  930.  
  931. {    Initialize the various Macintosh Managers.}
  932. {    Set default upper limits on window sizing.}
  933. {    FlushEvents does NOT toss disk insert events, so that disks}
  934. {    inserted while the application is starting up don't result}
  935. {    in dead drives.}
  936. {    NoMasters is the number of times to call MoreMasters.  gzProc is the address of a user - provided}
  937. {    grow zone function procedure to call if memory gets tight.  Pass nil if none to be used.    }
  938.  
  939.     procedure SkelInit;
  940.         var
  941.             i: integer;
  942.  
  943.     begin
  944.  
  945. { For non-Lightspeed Pascal users, the following inits are included as a compile time option, }
  946. {  See the $SETC definition at the beginning of the unit.  }
  947.  
  948. {$IFC UNDEFINED THINK_PASCAL }
  949.  
  950.         InitGraf(@thePort);
  951.         InitFonts;
  952.         InitWindows;
  953.         InitMenus;
  954.         TEInit;
  955.         InitDialogs(nil);
  956.         MaxApplZone;
  957. {$ENDC}
  958.  
  959.         FlushEvents(everyEvent - diskMask, 0);
  960.         for i := 1 to NoMasters do
  961.             MoreMasters;
  962.         if myGrowZone <> nil then
  963.             SetGrowZone(myGrowZone);
  964.  
  965.         InitCursor;
  966.         whList := nil;
  967.         whClobOnRmve := true;
  968.         SetRect(growRect, 80, 80, 512, 342 - mBarHeight);
  969.         mhList := nil;
  970.         mhClobOnRmve := true;
  971.         appleID := 0;
  972.         appleAboutProc := nil;
  973.         doAbout := false;
  974.         doneflag := 0;
  975.         pBkgnd := nil;
  976.         pEvent := nil;
  977.         pEventflag := false;
  978.         eventmask := everyEvent;
  979.         diskInitPt.v := 120;
  980.         diskInitPt.h := 100;
  981. {$IFC supportDialogs }
  982.         dlogEventMask := $16f;
  983. {$ENDC}
  984.  
  985. {    Set upper limits of window sizing to machine screen size.  Allow}
  986. {    for the menu bar.}
  987.  
  988.         GetWMgrPort(screenport);
  989.         growRect.right := screenPort^.portRect.right;
  990.         growRect.bottom := screenPort^.portRect.bottom - mBarHeight;
  991.  
  992. {    Set caching global variables to nil }
  993.  
  994.         oldWindow := nil;
  995.         oldWDHandler := nil;
  996.     end;
  997.  
  998. {    Main loop.}
  999.  
  1000. {    Task care of DA's with SystemTask.}
  1001. {    Run background task if there is one.}
  1002. {    If there is an event, check for an event hook.  If there isn't}
  1003. {    one defined, or if there is but it returns false, call the}
  1004. {    general event handler.  (Hook returns true if TransSkel should}
  1005. {    ignore the event.)}
  1006. {    If no event, call the "no-event" handler for the front window and for}
  1007. {    any other windows with idle procedures that are always supposed}
  1008. {    to run.  This is done in such a way that it is safe for idle procs}
  1009. {    to remove the handler for their own window if they want (unlikely,}
  1010. {    but...)  This loop doesn't check whether the window is really}
  1011. {    a dialog window or not, but it doesn't have to, because such}
  1012. {    things always have a nil idle proc.}
  1013. {    }
  1014. {    doneFlag is reset upon exit.  This allows it to be called}
  1015. {    repeatedly, or recursively.}
  1016.  
  1017. {    Null events are looked at (in SkelMain)}
  1018. {    and passed to the event handler.  This is necessary to make sure}
  1019. {    DialogSelect gets called repeatedly, or the caret won't blink if}
  1020. {    a dialog has any editText items.  Null events are not passed to any event-inspecting hook that may}
  1021. {    be installed.}
  1022.  
  1023.     procedure SkelMain;
  1024.  
  1025.         var
  1026.             theEvent: EventRecord;
  1027.             wh, wh2: WHandlerHnd;
  1028.             w: WindowPtr;
  1029.             haveEvent, testpevent, testbool: Boolean;
  1030.             tmpPort: GrafPtr;
  1031.             p: ProcPtr;
  1032.  
  1033.     begin
  1034.         while (doneFlag = 0) do
  1035.             begin
  1036.                 SystemTask;
  1037.                 if (pBkgnd <> nil) then
  1038.                     callpnoarg(pBkgnd);
  1039.                 haveEvent := GetNextEvent(eventMask, theEvent);
  1040.                 if (pEvent <> nil) then
  1041.                     testpevent := CallotherEvent(theEvent, pEvent)
  1042.                 else
  1043.                     testpevent := false;
  1044. { following line fixed from version 1.02 & 1.03     }
  1045.                 if haveEvent and ((pEvent = nil) or (testpevent = false)) then
  1046.                     DoEvent(theEvent);
  1047.                 if not haveEvent then
  1048.                     begin
  1049.                         wh := whList;
  1050.                         GetPort(tmpPort);
  1051.                         while (wh <> nil) do
  1052.                             begin
  1053.                                 wh2 := wh^^.whNext;
  1054.                                 w := wh^^.whWind;
  1055.                                 if ((w = FrontWindow) or not wh^^.whFrontOnly) then
  1056.                                     begin
  1057.                                         SystemTask;
  1058.                                         if (wh^^.whIdle <> nil) then
  1059.                                             begin
  1060.                                                 SetPort(wh^^.whWind);
  1061.                                                 p := wh^^.whIdle;
  1062.                                                 if (p <> nil) then
  1063.                                                     callpnoarg(p);
  1064.                                             end;
  1065.                                     end;
  1066.                                 wh := wh2;
  1067.                             end;
  1068.                         SetPort(tmpPort);
  1069.                     end;
  1070.             end;
  1071.         doneFlag := 0;
  1072.     end;
  1073.  
  1074. {    Tell SkelMain to stop}
  1075.  
  1076.     procedure SkelWhoa;
  1077.     begin
  1078.         doneFlag := 1;
  1079.     end;
  1080.  
  1081. {    Clobber all the menu, window and dialog handlers}
  1082.  
  1083.     procedure SkelClobber;
  1084.  
  1085.  
  1086.     begin
  1087.         oldWDHandler := nil;
  1088.         oldWindow := nil;
  1089.         while (whList <> nil) do
  1090.             begin
  1091.                 SkelRmveWind(whList^^.whWind);
  1092.             end;
  1093.         while (mhList <> nil) do
  1094.             begin
  1095.                 SkelRmveMenu(GetMHandle(mhList^^.mhID));
  1096.             end;
  1097.     end;
  1098.  
  1099. { -------------------------------------------------------------------- }
  1100. {                        Menu-handler interface routines                            }
  1101. { -------------------------------------------------------------------- }
  1102.  
  1103.  
  1104.  
  1105.  
  1106. {    Install handler for a menu.  Remove any previous handler for it.}
  1107. {    Pass the following parameters:}
  1108.  
  1109. {    theMenu    Handle to the menu to be handled.  Must be created by host.}
  1110. {    pSelect    Proc that handles selection of items from menu.  If this is}
  1111. {            nil, the menu is installed, but nothing happens when items}
  1112. {            are selected from it.}
  1113. {    pClobber Proc for disposal of handler's data structures.  Usually}
  1114. {            nil for menus that remain in menu bar until program}
  1115. {            termination.}
  1116.  
  1117. {    The menu is installed and drawn in the menu bar.}
  1118.  
  1119. {     Return false if no handler could be allocated, true if successful. }
  1120.  
  1121.     function SkelMenu;
  1122.         var
  1123.             mh: MHandlerHnd;
  1124.             myHand: Handle;
  1125.     begin
  1126.         mhClobOnRmve := false;
  1127.         SkelRmveMenu(theMenu);
  1128.         mhClobOnRmve := true;
  1129.         myHand := NewHandle(Sizeof(MHandler));
  1130.         SkelMenu := false;
  1131.         if myHand <> nil then
  1132.             begin
  1133.                 SkelMenu := true;                    { show we really got the memory }
  1134.                 mh := MHandlerHnd(myHand);
  1135.                 mh^^.mhNext := mhList;
  1136.                 mhList := MHandlerHnd(myHand);
  1137.                 mh^^.mhID := theMenu^^.menuID;    { get menu id number }
  1138.                 mh^^.mhSelect := pSelect;            { install selection handler }
  1139.                 mh^^.mhClobber := pClobber;        { install disposal handler }
  1140.                 InsertMenu(theMenu, 0);            { put menu at end of menu bar }
  1141.                 if DrawBar then
  1142.                     DrawMenuBar;
  1143.             end;
  1144.     end;
  1145.  
  1146. {    Remove a menu handler.  This calls the handler's disposal routine}
  1147. {    and then takes the handler out of the handler list and disposes}
  1148. {    of it.}
  1149.  
  1150. {    Note that the menu MUST be deleted from the menu bar before calling}
  1151. {    the clobber proc, because the menu bar will end up filled with}
  1152. {    garbage if the menu was allocated with NewMenu (see discussion of}
  1153. {    DisposeMenu in Menu Manager section of Inside Macintosh).}
  1154.  
  1155.     procedure SkelRmveMenu;
  1156.  
  1157.         var
  1158.             mID: integer;
  1159.             h, h2: MHandlerHnd;
  1160.             p: ProcPtr;
  1161.             returnflag: Boolean;
  1162.  
  1163.     begin
  1164.         mID := theMenu^^.menuID;
  1165.         returnflag := false;
  1166.         if mhlist <> nil then
  1167.             begin
  1168.                 if mhList^^.mhID = mID then
  1169.                     begin
  1170.                         h2 := mhlist;
  1171.                         mhList := h2^^.mhNext;
  1172.                     end
  1173.                 else
  1174.                     begin
  1175.                         h := mhList;
  1176.                         while (h <> nil) and not returnflag do
  1177.                             begin
  1178.                                 h2 := h^^.mhNext;
  1179.                                 if (h2 = nil) then
  1180.                                     begin
  1181.                                         h := nil;
  1182.                                         returnflag := true;
  1183.                                     end
  1184.                                 else if h2^^.mhID = mID then
  1185.                                     begin
  1186.                                         h^^.mhNext := h2^^.mhNext;
  1187.                                         h := nil;
  1188.                                     end;
  1189.                                 if h <> nil then
  1190.                                     h := h2;
  1191.                             end;
  1192.                     end;
  1193.                 if not returnflag then
  1194.                     begin
  1195.                         DeleteMenu(mID);
  1196.                         DrawMenuBar;
  1197.                         p := h2^^.mhClobber;
  1198.                         if mhClobOnRmve and (p <> nil) then
  1199.                             callpMenu(theMenu, p);
  1200.                         DisposHandle(Handle(h2));
  1201.                     end;
  1202.             end;
  1203.     end;
  1204.  
  1205. {    Install a handler for the Apple menu.}
  1206.  
  1207. {    SkelApple is called if TransSkel is supposed to handle the apple}
  1208. {    menu itself.  The title is the title of the first item.  If nil,}
  1209. {    then only desk accessories are put into the menu.  If not nil, then}
  1210. {    the title is entered as the first item, followed by a gray line,}
  1211. {    then the desk accessories.}
  1212.  
  1213. {     SkelApple does not cause the menubar to be drawn, so if the Apple menu is the only menu, }
  1214. {    DrawMenuBar must be called afterward.}
  1215.  
  1216. {    No value is returned, unlike SkelMenu.  It is assumed that SkelApple will be called so early in the}
  1217. {    application that the call to SkelMenu is virtually certain to succeed.  }
  1218.  
  1219.     procedure SkelApple;
  1220.  
  1221.         var
  1222.             appleTitle: Str255;
  1223.             dummy: boolean;
  1224.     begin
  1225.         appleTitle := ' ';
  1226.         appleTitle[1] := char($14);
  1227.         appleID := 1;
  1228.         AppleMenu := NewMenu(appleID, appleTitle);
  1229.         if aboutTitle <> '' then
  1230.             begin
  1231.                 doAbout := true;
  1232.                 AppendMenu(appleMenu, aboutTitle);
  1233.                 AppendMenu(appleMenu, '(-');
  1234.                 AppleAboutProc := aboutProc;
  1235.             end;
  1236.         AddResMenu(appleMenu, 'DRVR');
  1237.         dummy := SkelMenu(appleMenu, @DoAppleItem, @DoAppleClobber, false);
  1238.     end;
  1239.  
  1240. { -------------------------------------------------------------------- }
  1241. {                    Window-handler interface routines                    }
  1242. { -------------------------------------------------------------------- }
  1243.  
  1244.  
  1245.  
  1246. {    Install handler for a window.  Remove any previous handler for it.}
  1247. {    Pass the following parameters:}
  1248.  
  1249. {    theWind    Pointer to the window to be handled.  Must be created by host.}
  1250. {    pMouse    Proc to handle mouse clicks in window.  The proc will be}
  1251. {            passed the point (in local coordinates), the time of the}
  1252. {            click, and the modifier flags word.}
  1253. {    pKey    Proc to handle key clicks in window.  The proc will be passed}
  1254. {            the character and the modifier flags word.}
  1255. {    pUpdate    Proc for updating window.  TransSkel brackets calls to update}
  1256. {            procs with calls to BeginUpdate and EndUpdate, so the visRgn}
  1257. {            is set up correctly.  A flag is passed indicating whether the}
  1258. {            window was resized or not.  BY CONVENTION, the entire portRect}
  1259. {            is invalidated when the window is resized.  That way, the}
  1260. {            handler's update proc can redraw the entire content region}
  1261. {            without interference from BeginUpdate/EndUpdate.  The flag}
  1262. {            is set to false after the update proc is called; the}
  1263. {            assumption is made that it will notice the resizing and}
  1264. {            respond appropriately.}
  1265. {    pActivate Proc to execute when window is activated or deactivated.}
  1266. {            A boolean is passed to it which is true if the window is}
  1267. {            coming active, false if it's going inactive.}
  1268. {    pClose    Proc to execute when mouse clicked in close box.  Useful}
  1269. {            mainly to temp window handlers that want to know when to}
  1270. {            self-destruct (with SkelRmveWind).}
  1271. {    pClobber Proc for disposal of handler's data structures}
  1272. {    pIdle    Proc to execute when no events are pending.}
  1273. {    frontOnly True if pIdle should execute on no events only when}
  1274. {            theWind is frontmost, false if executes all the time.  Note}
  1275. {            that if it always goes, everything else may be slowed down!}
  1276.  
  1277. {    If a particular procedure is not needed (e.g., key events are}
  1278. {    not processed by a handler), pass nil in place of the appropriate}
  1279. {    procedure address.}
  1280.  
  1281. {    Return false  if no handler could be allocated, true if successful.}
  1282.  
  1283.     function SkelWindow;
  1284.  
  1285.         var
  1286.             hHand: WhandlerHnd;
  1287.  
  1288.     begin
  1289.         whClobOnRmve := false;
  1290.         SkelRmveWind(theWind);
  1291.         whClobOnRmve := true;
  1292.  
  1293. {    Get new handler, attach to list of handlers.  It is attached to the beginning of the list, which is simpler;}
  1294. {    the order should be irrelevant to the hose, anyway. }
  1295.  
  1296.         hHand := WHandlerHnd(NewHandle(Sizeof(WHandler)));
  1297.         SkelWindow := false;
  1298.         if hHand <> nil then
  1299.             begin
  1300.                 hHand^^.whNext := whList;
  1301.                 whList := hHand;
  1302.                 with hHand^^ do
  1303.                     begin
  1304.                         SkelWindow := true;                    { Show that we got the memory }
  1305.                         whWind := theWind;
  1306.                         whMouse := pMouse;
  1307.                         whKey := pKey;
  1308.                         whUpdate := pUpdate;
  1309.                         whActivate := pActivate;
  1310.                         whClose := pClose;
  1311.                         whClobber := pClobber;
  1312.                         whIdle := pIdle;
  1313.                         whFrontOnly := frontOnly;
  1314.                         whSized := false;
  1315.                         whGrow := GrowRect;
  1316.                     end;
  1317.             end;
  1318.         SetPort(theWind);
  1319.     end;
  1320.  
  1321. {    Remove a window handler.  This calls the handler's disposal routine}
  1322. {    and then takes the handler out of the handler list and disposes}
  1323. {    of it.}
  1324.  
  1325. {    SkelRmveWind is also called by SkelRmveDlog.}
  1326.  
  1327. {    Note that if the window cache variable is set to the window whose handler is being clobbered, the }
  1328. {    variable must be zeroed.    }
  1329.  
  1330.     procedure SkelRmveWind;
  1331.  
  1332.         var
  1333.             h, h2: WHandlerHnd;
  1334.             returnflag: Boolean;
  1335.  
  1336.     begin
  1337.         if theWind = oldWindow then
  1338.             begin
  1339.                 oldWindow := nil;
  1340. {•    oldWDHandler := nil;•}
  1341.             end;
  1342.  
  1343.         if (whList <> nil) then
  1344.             begin
  1345.                 returnflag := false;
  1346.                 if whList^^.whWind = theWind then
  1347.                     begin
  1348.                         h2 := whlist;
  1349.                         whList := whList^^.whNext;
  1350.                     end
  1351.                 else
  1352.                     begin
  1353.                         h := whList;
  1354.                         while (h <> nil) and not returnflag do
  1355.                             begin
  1356.                                 h2 := h^^.whNext;
  1357.                                 if (h2 = nil) then
  1358.                                     begin
  1359.                                         h := nil;
  1360.                                         returnflag := true;
  1361.                                     end
  1362.                                 else if h2^^.whWind = theWind then
  1363.                                     begin
  1364.                                         h^^.whNext := h2^^.whNext;
  1365.                                         h := nil;
  1366.                                     end;
  1367.                                 if h <> nil then
  1368.                                     h := h2;
  1369.                             end;
  1370.                     end;
  1371.                 if not returnflag then
  1372.                     begin
  1373.                         if (whClobOnRmve) then
  1374.                             DoClobber(h2);
  1375.                         DisposHandle(Handle(h2));
  1376.                     end;
  1377.             end;
  1378.     end;
  1379.  
  1380. {$IFC supportDialogs }
  1381.  
  1382. { -------------------------------------------------------------------- }
  1383. {                    Dialog-handler interface routines                                }
  1384. { -------------------------------------------------------------------- }
  1385.  
  1386.  
  1387.  
  1388. {    Install a dialog handler.  Remove any previous handler for it.}
  1389. {    SkelDialog calls SkelWindow as a subsidiary to install a window}
  1390. {    handler, then sets the event procedure on return.}
  1391.  
  1392. {    Pass the following parameters:}
  1393.  
  1394. {    theDialog    Pointer to the dialog to be handled.  Must be created}
  1395. {            by host.}
  1396. {    pEvent    Event-handling proc for dialog events.}
  1397. {    pClose    Proc to execute when mouse clicked in close box.  Useful}
  1398. {            mainly to dialog handlers that want to know when to}
  1399. {            self-destruct (with SkelRmveDlog).}
  1400. {    pClobber Proc for disposal of handler's data structures}
  1401.  
  1402. {    If a particular procedure is not needed, pass nil in place of}
  1403. {    the appropriate procedure address.}
  1404.  
  1405. {    Return false if no handler could be allocated, true if successful.}
  1406.  
  1407.     function SkelDialog;
  1408.  
  1409.         var
  1410.             wh: WHandlerHnd;
  1411.             aBool: Boolean;
  1412.  
  1413.     begin
  1414.         aBool := SkelWindow(theDialog, nil, nil, nil, nil, pClose, pClobber, nil, false);
  1415.         if aBool <> false then
  1416.             begin
  1417.                 wh := GetWDHandler(theDialog);
  1418.                 wh^^.whEvent := pEvent;
  1419.             end;
  1420.         SkelDialog := aBool;
  1421.     end;
  1422.  
  1423. {    Remove a dialog and its handler}
  1424.  
  1425.     procedure SkelRmveDlog;
  1426.  
  1427.     begin
  1428.         SkelRmveWind(theDialog);
  1429.     end;
  1430. {$ENDC}
  1431. { -------------------------------------------------------------------- }
  1432. {                    Miscellaneous interface routines                    }
  1433. { -------------------------------------------------------------------- }
  1434.  
  1435.  
  1436. {    Override the default sizing limits for a window, or, if theWind}
  1437. {    is nil, reset the default limits used by SkelWindow.}
  1438.  
  1439.     procedure SkelGrowBounds;
  1440.  
  1441.         var
  1442.             h: WHandlerHnd;
  1443.             r: Rect;
  1444.  
  1445.     begin
  1446.         if theWind = nil then
  1447.             SetRect(growRect, hLo, vLo, hHi, vHi)
  1448.         else
  1449.             begin
  1450.                 h := GetWHandler(theWind);
  1451.                 if h <> nil then
  1452.                     begin
  1453.                         SetRect(r, hLo, vLo, hHi, vHi);
  1454.                         h^^.whGrow := r;
  1455.                     end;
  1456.             end;
  1457.     end;
  1458.  
  1459. {    Set the event mask.}
  1460.  
  1461.     procedure SkelEventMask;
  1462.  
  1463.     begin
  1464.         eventMask := mask;
  1465.     end;
  1466.  
  1467. {    Return the event mask.}
  1468.  
  1469.     procedure SkelGetEventMask;
  1470.  
  1471.     begin
  1472.         mask := eventMask;
  1473.     end;
  1474.  
  1475. {    Install a background task.  If p is nil, the current task is}
  1476. {    disabled.}
  1477.  
  1478.     procedure SkelBackground;
  1479.  
  1480.     begin
  1481.         pBkgnd := p;
  1482.     end;
  1483.  
  1484. {    Return the current background task.  Return nil if none.}
  1485.  
  1486.     procedure SkelGetBackground;
  1487.     begin
  1488.         p := pBkgnd;
  1489.     end;
  1490.  
  1491. {    Install an event-inspecting hook.  If p is nil, the hook is}
  1492. {    disabled.}
  1493.  
  1494.     procedure SkelEventHook;
  1495.  
  1496.     begin
  1497.         pEvent := p;
  1498.     end;
  1499.  
  1500.     procedure SkelGetEventHook;
  1501.  
  1502.     begin
  1503.         p := pEvent;
  1504.     end;
  1505. {$IFC supportDialogs }
  1506.  
  1507. {    Set the mask for event types that will be passed to dialogs.}
  1508. {    Bit 1 is always set, so that null events will be passed.}
  1509. {    If this is not done, the caret does not blink in editText items.}
  1510.  
  1511.     procedure SkelDlogMask;
  1512.  
  1513.     begin
  1514.         dlogEventMask := BitOr(mask, 1);
  1515.     end;
  1516.  
  1517. {    Return the current dialog event mask.}
  1518.  
  1519.     procedure SkelGetDlogMask;
  1520.  
  1521.     begin
  1522.         mask := dlogEventMask;
  1523.     end;
  1524. {$ENDC}
  1525. end.